"""
资产列表首页
资产展示的是job列表
"""
import copy
import json
import re

from app.libs.ip import check_source_ip
from app.libs.redprint import RedPrint
from app.libs.regexp import IP_RE
from app.libs.score import Score
from app.libs.enums import AssetType, TaskType, WS_TASK_GROUP_TUPLE, AVAILABLE_TASK_GROUP_TUPLE
from app.db.models.jobs import Job
from app.db.models.users import User
from flask import request, jsonify, g
from app.api.v2 import asset_util
from app.api.v2.common import query_data, param_input
from app.handler.v2_jobs import JobVerifyHelper
from app.validators.asset import AssetJobId, AssetStatus, BatchAssetJobId, \
    HostAssetAdd, AssetAddForm, AssetUpdateForm, AssetListForm, AssetVerifiedFrom
from app.validators.asset_settings import SingleConfig, PeriodConfig
from app.auth import permission_required
from app.errors import ParameterError
from app.log.logs import CAssetLog

api = RedPrint('asset')


@api.route('/verified-asset', methods=['GET'])
def asset_verified():
    """报表页面 资产下拉列表"""
    uid = g.audit_uid

    form = AssetVerifiedFrom().validate_for_api()
    asset_type = form.asset_type
    if g.role.name == '管理员':
        uid = form.uid

    resp_fields = ['note', 'target', 'targetUrl', 'id', 'sourceIp', 'alertSettings.alertGroupIds', 'assetType']
    q = {'uid': uid, 'verification.isVerified': True, "assetType": {"$ne": "host"}}

    if asset_type == 'all':
        del q['assetType']
    elif asset_type == 'host':
        q['assetType'] = 'host'

    results, count = query_data(Job.objects, q, resp_fields, sort=['-id'])
    if asset_type in ['all', 'host']:
        for item in results:
            if item['assetType'] == 'host':
                item['sourceIp'] = ''

    return jsonify({'code': 200, 'data': {'JobMany': results}})


@api.route('/verified-asset-ipv6', methods=['GET'])
def asset_verified_ipv6():
    resp_fields = ['note', 'target', 'targetUrl', 'id', 'uid', 'sourceIp']
    q = {
        'verification.isVerified': True,
        "assetType": {"$ne": "host"},
        'target.hostname': {'$not': {'$regex': f'.+:.+|{IP_RE.pattern}'}}
    }
    if g.role.name == '普通用户':
        q['uid'] = g.audit_uid
    results, count = query_data(Job.objects, q, resp_fields, sort=['-id'])
    return jsonify({'code': 200, 'results': {'JobMany': results}})


@api.route('/asset_list', methods=['GET'])
def asset_list():
    """资产列表"""
    resp_fields = [
        'id', 'assetId', 'enableAlert', 'enableMonitor', 'note', 'sourceIp',
        'target', 'targetUrl', 'verification',
        'uid', 'createTime', 'securityStatus', 'isSupportIpv6',
    ]
    for t in WS_TASK_GROUP_TUPLE + AVAILABLE_TASK_GROUP_TUPLE + (TaskType.asset.value, ):
        resp_fields.append(f"{t}ResultId")
    form = AssetListForm().validate_for_api()
    query = form.data

    asset_type = query['asset_type']
    q = {'uid': g.audit_uid, 'assetType': asset_type}

    if asset_type != 'host':
        q['assetType'] = {'$ne': 'host'}

    if query.get('note') is not None:
        q['note'] = {'$regex': re.escape(query['note'])}
    if query.get('target_url') is not None:
        q['targetUrl'] = {'$regex': re.escape(query['target_url'])}
    if query.get('source_ip') is not None:
        q['sourceIp'] = {'$regex': re.escape(query['source_ip'])}
    if query.get('level') is not None:
        q['securityStatus.level'] = query['level']
    if query.get('is_verified') is not None:
        q['verification.isVerified'] = query['is_verified']
    monitor_type = query.get('monitor_type')
    task_level = query.get('task_level', None) if asset_type != AssetType.host.value else None
    if monitor_type and asset_type != AssetType.host.value and Score.check_level_num(task_level):
        q.update({f"{monitor_type}ResultId": {"$exists": 1, "$nin": ["", None]}})
    page, count = query['page'], query['count']
    sort = ['-id']
    results, total = asset_util.query_asset_list(
        Job.objects, q, resp_fields, page, count, sort, monitor_type, task_level)
    return jsonify({'data': {'results': results, 'count': total}})


@api.route('/admin_asset_list', methods=['GET'])
def admin_asset_list():
    """资产列表"""
    form = AssetListForm().validate_for_api()
    result = asset_util.admin_asset_list(form.data)
    return jsonify({'code': 0, 'message': 'ok', 'data': result})


@api.route('/asset_list_download', methods=['POST'])
def asset_list_download():
    """导出资产列表"""
    form = AssetListForm().validate_for_api()
    query = copy.deepcopy(form.data)
    query['page'] = 0
    jobs = asset_util.admin_asset_list(query).get('results', [])
    result = asset_util.get_asset_list_csv(jobs)

    return jsonify({'code': 200, 'message': "ok", 'data': result})


@api.route('/asset_add', methods=['POST'])
def asset_add():
    """添加Web资产"""
    form = AssetAddForm().validate_for_api()

    uid, username = g.audit_uid, g.user.username
    if g.role.name == '管理员':
        user = User.objects.find_one({"_id": form.uid})
        uid = user.id
        username = user.username

    result = {'summary': {}, 'detail': [], 'success': []}

    note = form.note
    target_url = form.target_url
    source_ip_list = form.source_ips

    affects = [target_url]
    for source_ip in source_ip_list:
        status, message = check_source_ip(source_ip)
        if not status:
            result['detail'].append({"note": note, "target_url": target_url, "source_ip": source_ip,
                                     "asset_type": "Web资产", "reason": message})
            continue

        temp_data = {'note': note, 'sourceIp': source_ip, 'url': form.url.url}
        status, message = asset_util.add_asset(temp_data, uid, username)
        if not status:
            result['detail'].append({"note": note, "target_url": target_url, "source_ip": source_ip,
                                     "asset_type": "Web资产", "reason": message})
        else:
            result['success'].append({"note": note, "target_url": target_url, "source_ip": source_ip,
                                      "asset_type": "Web资产", "reason": "添加成功"})

    if not source_ip_list:
        source_ip = ''
        temp_data = {'note': note, 'sourceIp': source_ip, 'url': form.url.url}
        status, message = asset_util.add_asset(temp_data, uid, username)
        if not status:
            result['detail'].append({"note": note, "target_url": target_url, "source_ip": source_ip,
                                     "asset_type": "Web资产", "reason": message})
        else:
            result['success'].append({"note": note, "target_url": target_url, "source_ip": source_ip,
                                      "asset_type": "Web资产", "reason": "添加成功"})

    total = len(source_ip_list) if source_ip_list else 1
    fail = len(result['detail'])
    code = 100 if fail else 0
    success = total - fail
    msg = '资产添加失败' if fail else '资产添加成功'
    result['message'] = msg
    result['status'] = "error" if fail else "success"
    result['summary'] = {"total": total, "success": success, "fail": fail}

    opt_log_details = [f'资产IP地址: {item["source_ip"]}, 结果: {item["reason"]}' for item in result["detail"]]

    detail = "新增Web资产, 资产名称: {}, 资产地址: {}, ".format(note, target_url)
    if result["success"]:
        detail += "添加成功的资产地址: {}".format(",".join([item.get("source_ip") for item in result["success"]]))
    if opt_log_details:
        detail += ", 添加失败的详情: {}".format("; ".join(opt_log_details))

    log_info = {
        'affects': affects,
        'actionMessage': '新增Web资产',
        'detail': detail
    }
    CAssetLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    if "success" in result:
        del result["success"]

    source_ip_length = len(source_ip_list)
    if fail == 0 and source_ip_length in (0, 1):
        detail = {
            "note": note,
            "target_url": target_url,
            "job_id": str(message.id),
            "source_ip": message.sourceIp if message.sourceIp else "",
            "is_verified": message.verification.isVerified,
            "challenge": message.verification.challenge,
        }
        result["detail"].append(detail)

    return jsonify({'code': code, 'message': msg, 'data': result})


@api.route('/host_asset_add', methods=['POST'])
@param_input(HostAssetAdd, param='json')
def host_asset_add(data):
    """添加主机资产"""
    uid, username = g.audit_uid, g.user.username

    if g.role.name == '管理员':
        uid = data.get('uid')
        user = User.objects.find_one({"_id": uid})
        username = user.username

    affects = []
    result = {'summary': {}, 'detail': [], 'success': []}
    note = data.get('note')
    asset_type = data.get('asset_type')
    target_urls = data.get('target_urls', [])
    if len(target_urls) > 100:
        raise ParameterError(msg='一次最多只能添加100个')

    for target_url in target_urls:
        affects.append(target_url)
        status, message = check_source_ip(target_url)
        if not status:
            result['detail'].append({"note": note, "target_url": target_url, "asset_type": "主机资产", "reason": message})
            continue

        temp_data = {'note': note, 'sourceIp': target_url, 'assetType': asset_type}
        status, message = asset_util.add_host_asset(temp_data, uid, username)
        if not status:
            result['detail'].append({"note": note, "target_url": target_url, "asset_type": "主机资产", "reason": message})
        else:
            result['success'].append({"note": note, "target_url": target_url, "asset_type": "主机资产", "reason": "添加成功"})

    total = len(target_urls)
    fail = len(result['detail'])
    code = 100 if fail else 0
    success = total - fail
    message = '资产添加失败' if fail else '资产添加成功'
    result['message'] = message
    result['status'] = "error" if fail else "success"
    result['summary'] = {"total": total, "success": success, "fail": fail}

    opt_log_details = [f'资产地址: {item["target_url"]}, 结果: {item["reason"]}' for item in result["detail"]]
    log_info = {
        'affects': affects,
        'actionMessage': '新增主机资产',
        'detail': f'新增主机资产, 资产名称: {note}, '
                  f'添加成功的资产地址: {",".join([item.get("target_url") for item in result["success"]])}, '
                  f'添加失败的详情: {"; ".join(opt_log_details)}'
    }
    CAssetLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    if "success" in result:
        del result["success"]

    return jsonify({'code': code, 'message': message, 'data': result})


@api.route('/admin_asset_batch_add', endpoint='admin_asset_batch_add', methods=['POST'])
@permission_required('admin')
def admin_asset_batch_add():
    """添加资产"""

    data = json.loads(request.data)

    asset_type = data.get('asset_type', '')
    if asset_type not in ('web', 'host'):
        raise ParameterError(msg='资产类型不合法')

    data_list = data.get('data_list', [])
    if not data_list:
        raise ParameterError(msg='任务为空')

    if len(data_list) > 500:
        raise ParameterError('为了保证任务分散, 一次最多可以提交500条')

    code, message, data = asset_util.async_batch_add_asset(data_list, asset_type)

    return jsonify({'code': code, 'message': message, 'data': data})


@api.route('/asset-update', methods=['POST'])
def asset_update():
    """资产编辑 只允许编辑 note、sourceIp"""
    form = AssetUpdateForm().validate_for_api()
    uid = g.audit_uid
    if g.role.name == '管理员':
        uid = form.uid

    update = {}
    if form.note is not None:
        update['note'] = form.note
    if isinstance(form.sourceIp.data, str) and not form.sourceIp.data:
        update['sourceIp'] = ''
    elif isinstance(form.sourceIp.data, str):
        update['sourceIp'] = form.check_source_ip(form.sourceIp.data)

    flag, msg = asset_util.update_asset(uid, form.jobId, update)
    if not flag:
        raise ParameterError(msg=msg)
    return jsonify({'code': 0, 'message': '修改成功'})


@api.route('/asset-del', methods=['POST'])
@param_input(BatchAssetJobId, param='json', skip_defaults=True)
def asset_del(data):
    """资产删除"""
    uid = g.audit_uid
    if g.role.name == '管理员':
        uid_list = data.get('uid_list')
        count = 0
        for uid in uid_list:
            job_ids = data['jobId']
            count += asset_util.del_asset(uid, job_ids)
    else:
        job_ids = data['jobId']
        count = asset_util.del_asset(uid, job_ids)
    return jsonify({'code': 0, 'message': '删除资产成功', 'data': count})


@api.route('/asset-status', methods=['POST'])
@param_input(AssetStatus, param='json', skip_defaults=True)
def asset_status(data):
    """资产状态修改"""
    uid = g.audit_uid
    if g.role.name == '管理员':
        uid = data.get('uid')

    job_id = data['jobId']
    update_field = {}
    if data.get('enableMonitor') is not None:
        update_field['enableMonitor'] = data['enableMonitor']
    elif data.get('enableAlert') is not None:
        update_field['enableAlert'] = data['enableAlert']
    asset_util.update_asset_status(uid, job_id, update_field)
    return jsonify({'code': 0, 'message': '资产状态修改成功'})


@api.route('/asset-verify', methods=['POST'])
@param_input(AssetJobId, param='json', skip_defaults=True)
def asset_verify(data):
    """资产认证"""
    job_id = data['jobId']
    enable_task = data.get('enableTask')
    verify_obj = JobVerifyHelper(job_id=job_id, uid=g.user.id)
    is_owner = verify_obj.user_asset_verify(g.user.username)
    if enable_task and is_owner:
        asset_util.switch_job_tasks(verify_obj.job, True)
    if is_owner:
        return jsonify({'code': 0, 'message': '资产状态校验成功'})
    return jsonify({'code': -1, 'message': '资产校验失败'})


@api.route('/config-period', methods=['POST'])
@param_input(PeriodConfig, param='json', skip_defaults=True)
def config_period(data):
    """周期配置管理"""
    uid = g.audit_uid
    jobId = data['jobId']
    asset_util.config_period(uid, jobId, data)


@api.route('/config-single', methods=['POST'])
@param_input(SingleConfig, param='json', skip_defaults=True)
def config_single(data):
    """单次配置管理"""

    uid = g.audit_uid
    jobId = data['jobId']
    resp = asset_util.config_single(uid, jobId, data)
    return jsonify({'code': 0, 'message': '单次任务下发成功'})
